From 5981df49527a742cd45aaf9d7a08ea8da262ea68 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 30 Mar 2026 10:35:55 -0700 Subject: [PATCH] CVE-2026-5223: prohibit unpacking symlinks and other unexpected entries MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Cargo has historically not allowed creating .crate packages containing symlinks. (It packages the symlink target in place of the symlink, instead.) So, any package containing a symlink would have to be hand-constructed. Such packages are also not allowed on crates.io, so it could only come from an alternate registry. Rather than dealing with symlink traversal attacks when unpacking a crate, just prohibit symlinks entirely. In the process, also prohibit other kinds of unusual entries. As an exception, allow character devices but warn about them, because some exist in crates on crates.io. FG: adapted paths Signed-off-by: Fabian Grünbichler Gbp-Pq: Topic cargo Gbp-Pq: Name CVE-2026-5223-prohibit-unpacking-symlinks-and-other-unexp.patch --- .../cargo/src/cargo/sources/registry/mod.rs | 10 ++++- src/tools/cargo/tests/testsuite/registry.rs | 39 ++++++++----------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/tools/cargo/src/cargo/sources/registry/mod.rs b/src/tools/cargo/src/cargo/sources/registry/mod.rs index 04d4ec304a..d152a1e21a 100644 --- a/src/tools/cargo/src/cargo/sources/registry/mod.rs +++ b/src/tools/cargo/src/cargo/sources/registry/mod.rs @@ -197,7 +197,7 @@ use cargo_util::paths::{self, exclude_from_backups_and_indexing}; use flate2::read::GzDecoder; use serde::Deserialize; use serde::Serialize; -use tar::Archive; +use tar::{Archive, EntryType}; use tracing::debug; use crate::core::dependency::Dependency; @@ -1088,6 +1088,14 @@ fn unpack( ) } + // Prevent unpacking symlinks and other unexpected entry types + match entry.header().entry_type() { + EntryType::Regular | EntryType::Directory => {} + t => anyhow::bail!( + "invalid tarball downloaded, contains an entry at {entry_path:?} with invalid type {t:?}", + ), + } + // Prevent unpacking the lockfile from the crate itself. if entry_path .file_name() diff --git a/src/tools/cargo/tests/testsuite/registry.rs b/src/tools/cargo/tests/testsuite/registry.rs index 5a30dad646..4a4e83663b 100644 --- a/src/tools/cargo/tests/testsuite/registry.rs +++ b/src/tools/cargo/tests/testsuite/registry.rs @@ -3274,8 +3274,7 @@ fn package_lock_inside_package_is_overwritten() { } #[cargo_test] -fn package_lock_as_a_symlink_inside_package_is_overwritten() { - let registry = registry::init(); +fn package_lock_as_a_symlink_inside_package_is_invalid() { let p = project() .file( "Cargo.toml", @@ -3298,21 +3297,23 @@ fn package_lock_as_a_symlink_inside_package_is_overwritten() { .symlink(".cargo-ok", "src/lib.rs") .publish(); - p.cargo("check").run(); + p.cargo("check") + .with_status(101) + .with_stderr_data(str![[r#" +[UPDATING] `dummy-registry` index +[LOCKING] 1 package to latest compatible version +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[ERROR] failed to download replaced source registry `crates-io` - let id = SourceId::for_registry(registry.index_url()).unwrap(); - let hash = cargo::util::hex::short_hash(&id); - let pkg_root = paths::cargo_home() - .join("registry") - .join("src") - .join(format!("-{}", hash)) - .join("bar-0.0.1"); - let ok = pkg_root.join(".cargo-ok"); - let librs = pkg_root.join("src/lib.rs"); +Caused by: + failed to unpack package `bar v0.0.1 (registry `dummy-registry`)` - // Is correctly overwritten and doesn't affect the file linked to - assert_eq!(ok.metadata().unwrap().len(), 7); - assert_eq!(fs::read_to_string(librs).unwrap(), "pub fn f() {}"); +Caused by: + invalid tarball downloaded, contains an entry at "bar-0.0.1/.cargo-ok" with invalid type Symlink + +"#]]) + .run(); } #[cargo_test] @@ -4751,13 +4752,7 @@ Caused by: failed to unpack package `bar v1.0.0 (registry `dummy-registry`)` Caused by: - failed to unpack entry at `bar-1.0.0/smuggled` - -Caused by: - failed to unpack `[ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0/smuggled` - -Caused by: - [..] when creating dir [ROOT]/home/.cargo/registry/src/-[HASH]/bar-1.0.0/smuggled + invalid tarball downloaded, contains an entry at "bar-1.0.0/smuggled" with invalid type Symlink "#]]) .run(); -- 2.30.2